
package rendava;
import java.util.Properties;
import java.io.File;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.FileInputStream;
import java.util.ArrayList;


/**
 * Parser. This is where the tree is created. An input file is read into the class and turned into a tree.
 * @author 
 * @version 0.8
 */
public class Parser 
{
	private File htmlFile;
	private int tagDepth;
	private String line;
	private Tag currentTag;
	private String tagData;
	private Tree tree;
	private BufferedReader input;
	//holds the tag to be added to stack
	private ArrayList<Tag> Ttag;
	private int ptag = -1;
	
	//used to hold the current font properties in use
	//used as a stack each time </Font> is seen the props are taken off and reverted back to previous 
	//prop in stack
	private ArrayList<Fontprops> fprops;
	private int fcount = -1;
	//Font properties are used when a new text is added to the tree
	private String fontFace = "Courier";
	private String fontColour = "black";
	private int fontSize = 12;
	private boolean bold = false;
	private boolean italic = false;
	private boolean head = false;
	// holds the value of the header tag seen e.g <h1>, <h2> it would hold 1 or 2
	private int headv;
	// props file used to hold tags 
	private Properties props;
	

	
	/**
	 * Creates tag stack and props stack. also loads properties file into props variable
	 * @param filepath of the HTML file.
	 * 
	 */
	public Parser(String filepath) 
	{
		tagDepth = 0;
		
		
		fprops = new ArrayList<Fontprops>();
		Ttag = new ArrayList<Tag>();
		
		// loads properties file into props
		props = new Properties();
		try
        {
          props.load(new FileInputStream("tags"));
            
            
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
		
		
		//tree = new Tree(); tree cannot be initialised here, since the Tree constructor 
		//					 needs a root element at the start. Perhaps this should be changed?
		
		loadHTML(new File(filepath));
		
		
		
	}
	
	
	
	/**
	 * Loads the file into a variable then reads the first line.
	 * @param filepath of the HTML file.
	 * 
	 */
	public void loadHTML(File fileName) 
	{
		if(!fileName.exists()) {
			System.err.println("Non existent properties file");
			System.exit(-1);
		}
		
		htmlFile = fileName;
		try {
			// reads file into a variable
			input = new BufferedReader(new FileReader(htmlFile));
		}
		catch(IOException e)
		{
			System.err.println("File not found or other error");
		}
		// reads the file
		readNextLine();
	}
	
	
	/**
	 * Reads the html file a line at a time converting each line in the file to a string
	 * then checking for tags. If <html> has not been seen then error message is output. 
	 * 
	 */
	public void readNextLine()
	{
		// used as a flag to indicate when to start reading file e.g when <html> is first seen
		boolean fstart = false; 
		try {
	        	        /*
	        * readLine is a bit quirky :
	        * it returns the content of a line MINUS the newline.
	        * it returns null only for the END of the stream.
	        * it returns an empty String if two newlines appear in a row.
	        */
			while((line = input.readLine()) != null)
			{
				//checks to see if <html> has been seen
				if (line.indexOf("<html>") != -1 || fstart)
				{
				checkLine(line);
				fstart = true; // if true checks the rest of the file after seeing <html>
				}
			}
	        	input.close();
	        	if (fstart == false) //if <html> has not been seen out put error
	        	{
	        		System.out.println("File could not be turned into a tree <html> not found!!!");
	        	}
	      }
		catch (IOException ex){
		      ex.printStackTrace();
		    }
	    
	}    
	
	
	public String cleanstring(String line)
	{
		StringBuilder temp = new StringBuilder(); 
		int j = 0;
		char ch;
		while(j < line.length())
		{
			ch =  line.charAt(j);
			
			if (ch != '\t')
			{
					
					temp.append(ch);
			}
			j++;
			
		}
		
		return temp.toString();
	}
	
	/**
	 * reads through line looking for tags if seen adds them to the tree.
	 * @param line String which holds a line in the file. 
	 * 
	 */
	public void checkLine(String line)
	{
		line = cleanstring(line);
		
		// pointer to the position in the string
		int j = 0;
		
		// while not end of string
		while (j < line.length())
		{
			
			char ch;
			ch = line.charAt(j); // puts current character in string into a variable 
			// used when < has been seen after seeing unneeded text e.g kljdhfljksdhlf then <
			branch:
			if(ch == '<')
			{
				// used to build a string from character seen so far
				StringBuilder temp = new StringBuilder(); 
				
				temp.append(ch); // adds character to end of string
				j++;
				if (j < line.length()-1)
				{
					
					ch = line.charAt(j);
					while(ch != '<' && ch != '>') // keeps building up string until < > is seen
					{
						temp.append(ch);
						j++;
						if(j > line.length()-1)
						{
							break; //if end of the line is seen jumps out loop.
						}
							
						ch = line.charAt(j);
						
						
					}
					
					if( ch == '<') //if have seen a < after seeing <asdasa then go to branch
					{
						break branch;
					}
					
					if (ch == '>') // if you have seen  a close tag so so far you have seen e.g <html> 
					{
						temp.append(ch);
						j++;
						String Stag = temp.toString(); // turns what we have seen so far to a string
						// used to compare what Stag holds
						String font = "<font "; 
						String heder = "<h";
						String img = "<img ";
						
						if(Stag.startsWith(font))// if Stag start with <font want to see if any attributes are inside
						{
							
							DisplayFont ftag = new DisplayFont(); // creates a new create font tag
							checkattribute(Stag); // checks attributes of font tag 
							ptag++;
							Ttag.add(ftag);// tag added to the stack
						}
						else if(props.getProperty(Stag) != null) //if Stag is in the properties file goes into statement
						{
							Tag tag = null;
							try
					        {	
								
					            tag = (Tag) Class.forName(props.getProperty(Stag)).newInstance(); // creates a tag object from the properties file 
					        }
					        catch(Exception e)
					        {
					            e.printStackTrace();
					        }
					        
					        if (Stag.equals("<html>"))
					        {
					        	tree = new Tree((Tag)tag);
					        	
					        }
					        
					        if (Stag.equals("<b>")) 
					        {
					        	// if tag equals <b> sets flag to true
					        	bold = true;
					        }
					        else if (Stag.equals("<i>") )
					        {
					        	italic = true;
					        }
					       
					        // if tag is self closing no need to add to stack 
					        if (tag.closeTag().equals("close"))
					        {
					        	tree.addTag((Tag) tag, Ttag.get(ptag)); // adds tag to tree
					        	
					        }
					        else
					        {
					        ptag++;
					        Ttag.add((Tag)tag); // adds a tag to stack
					        }
					        
						}
						else if (Stag.startsWith(img))
						{
							// looks for src= in the img tag
							if(Stag.indexOf("src=") != -1)
							{
								int p = Stag.indexOf("src=");
								// looks for two occurences of " in the tag
								if(Stag.charAt(p+4) == '"')
								{
									if (Stag.indexOf('"',p+5) != -1)
									{
										int k = Stag.indexOf('"',p+5);
										Image imag = new Image();
										
										imag.seturl(Stag.substring(p+5, k-1)); // sets the url of the image as the substring found in "
										tree.addTag(imag,Ttag.get(ptag)); // adds img to tree because it is self closing

									}
								}
								
							}
							
						}
						else if (Stag.startsWith(heder)) 
						{
							if (Character.isDigit(Stag.charAt(2))) // sees if character at <h? is a number
							{
								int snum = Integer.parseInt(Stag.substring(2, 3)); //changes character at <h? int a number
								
								if (snum >0 && snum< 7) // can only set size for h tags between 1 and 6
								{
									head = true;
									headv = snum; // puts header value in variable
									Header htag = new Header(); 
									htag.setheadsize(snum); // sets the head size of the font 
									ptag++;	
									Ttag.add(htag);
								}
							}
						} 
						else if(Stag.charAt(1) == '/') // if tag has a / to indicate it could be a close tag
						{
							int stackp;
							
							for(stackp = ptag; stackp > 0; stackp--) // goes through stack looking for close tag
							{
								if(Stag.equals(Ttag.get(stackp).closeTag())) // checks to see if current object looked at is Stag
								{
									//if close bose is seen any tag that has not been closed needs to be added to the tree
									if (Stag.equals("</body>"))
									{
										for (int i=ptag; i> stackp;i--)
										{
											tree.addTag(Ttag.get(i), Ttag.get(i- 1)); // adds tag to tree with root tag
											Ttag.remove(i);// removes the tag from the tree
											ptag--;
										}
									}
									tree.addTag(Ttag.get(stackp), Ttag.get(stackp- 1)); // adds tag to tree with root tag
									Ttag.remove(stackp);// removes the tag from the tree
									ptag--;// 
									
									if(Stag.equals("</font>")) 
									{
										int k = fprops.size(); // gets the size of the props array 
										// removes the font properties from the top of array so last fp can get set
										fprops.remove(k-1);
										
										if (fprops.isEmpty()) // if array empty change font back to original 
										{
											fontFace = "Courier";
											fontColour = "black";
											fontSize = 12;
										}
										else// reverts font props back to what is on the top of the stack
										{
											fontFace = fprops.get(k-1).getSface();
											fontColour = fprops.get(k-1).getScolour();
											fontSize = fprops.get(k-1).getSsize();
										}
										
									}
									else if(Stag.equals("</b>")) //
									{
										bold = false;
									}
									else if (Stag.equals("</i>"))
									{
										italic = false;
									}
									else if (Stag.equals("</h" + headv + ">")) // if Stag equals head tag font size changed back 
									{
										head = false;
										int k = fprops.size();
										if (!fprops.isEmpty())
										{
										fontSize = fprops.get(k-1).getSsize();
										}
										else
											fontSize = 12; 
										
									}
									break; // jumps out loop once found
								}
								
							}
						}
					}
				}
				
			}
			else if(ch != ' ') //if the character is not space   
			{
				// creates a string builder which will be used to create a text
				StringBuilder temp = new StringBuilder();
				
				temp.append(ch);
				j++;
				if (j < line.length())
				{
					
					ch = line.charAt(j);
					while(ch != '<')
					{
						temp.append(ch);
						j++;
						if(j > line.length()-1)
						{
							break;
						}
						ch = line.charAt(j);
					}
					String Stag = temp.toString(); //turns substring into a real string
					Createtext(Stag); // passes string into create text so font tag can be made
							
				}
			}
			else // if space is seen moves onto next char. 
			{
				j++;
			}
			
			
		}
		
	}
	
	
	/**
	 * Creates a font tag out of the string passed in using the current font properties.
	 * @param text text passed in to be turned into a font tag.
	 * 
	 */
	public void Createtext(String text)
	{
		DisplayFont ftag = new DisplayFont(); 
		
		ftag.setText(text); // sets the text of the tag
		ftag.setSize(fontSize); 
		ftag.setFontColour(fontColour);
		ftag.setFontFace(fontFace);
		if (bold) // if bold has been set text is set to bold
		{
			ftag.setBold();
		}
		
		if(italic)
		{
			ftag.setItalic();
		}
		
		if(head) // head is true sets the size of the font
		{
			if (headv == 1)
			{
				ftag.setSize(24);
			}
			else if (headv == 2)
			{
				ftag.setSize(20);
			}
			else if (headv == 3)
			{
				ftag.setSize(18);
			}
			else if (headv == 4)
			{
				ftag.setSize(14);	
			}
			else if (headv == 5)
			{
				ftag.setSize(12);
			}
			else if (headv == 6)
			{
				ftag.setSize(10);
			}
		}
		//setting need to be done for bold and italic or head
		
		tree.addTag(ftag, Ttag.get(ptag));// adds the font to tree
		
	}
	
	
	/**
	 * checks the attributes of a <font> tag looks for different attributes and sets them 
	 * in the font props object also sets show that they have been set.  
	 * @param strin String which holds the <font tag and its attributes 
	 * 
	 */
	public void checkattribute(String strin)
	{
		String size = "size=";
		String face = "face=";
		String colour = "color=";
		
		Fontprops fprop = new Fontprops();
		
		if(strin.indexOf(size) != -1) // checks to see if size= is in the font tag
		{
			int j = strin.indexOf(size);
			
			if(strin.charAt(j + 5) == '"')
			{
				if(Character.isDigit(strin.charAt(j +6))) // if after seeing size=" there is a number
				{
					if (Character.isDigit(strin.charAt(j +7))) // if after seeing size="8 there is another number
					{
						if (strin.charAt(j +8) == '"')
						{
							int snum = Integer.parseInt(strin.substring(j+6, j+8)); // changes string number in quotes to a real number
							//maximum font size is 50 
							if (snum < 50)
							{
							fontSize = snum; 
							fprop.setSsize(snum);
							fprop.setsize(true);
							}
						}
					}
					else if (strin.charAt(j +7) == '"') // if after seeing size="8 there is a close 
					{
						
						int snum = Integer.parseInt(strin.substring(j+6, j+7));// turns char into a number 
						
						
						fontSize = snum; 
						fprop.setSsize(snum);
						fprop.setsize(true);
						
					}
				}			
			}
		}
		if (strin.indexOf(colour) != -1) // searches for colour in font tag
		{
			int j = strin.indexOf(colour); // puts the index of where colour starts in j 
			
			if(strin.charAt(j + 6) == '"')// checks to see if " after seeing color=
			{
				if (strin.indexOf('"',j+7) != -1) // checks to see if second " is after colour e.g color="red"
				{
					int k = strin.indexOf('"',j+7);
					String cstring = strin.substring(j+7, k); // creates a sub string of contents of " "
					
					if (cstring.equalsIgnoreCase("red")) // if substring is equal to red puts it in font state
					{
						fontColour = cstring;
						fprop.setScolour(cstring);
						fprop.setcolour(true);
					}
					else if(cstring.equalsIgnoreCase("yellow"))
					{
						fontColour = cstring;
						fprop.setScolour(cstring);
						fprop.setcolour(true);
					}
					else if (cstring.equalsIgnoreCase("blue"))
					{
						fontColour = cstring;
						fprop.setScolour(cstring);
						fprop.setcolour(true);
					}
					else if (cstring.equalsIgnoreCase("green"))
					{
						fontColour = cstring;
						fprop.setScolour(cstring);
						fprop.setcolour(true);
					}
					else if (cstring.equalsIgnoreCase("black"))
					{
						fontColour = cstring;
						fprop.setScolour(cstring);
						fprop.setcolour(true);
					}		
				}	
						
			}
		}
		if (strin.indexOf(face) != -1) // searches for face in font tag
		{
			int j = strin.indexOf(face); // puts the index of where face starts in j 
			
			if(strin.charAt(j + 5) == '"')// checks to see if " after seeing face=
			{
				if (strin.indexOf('"',j+7) != -1) // checks to see if second " is after colour 
				{
					int k = strin.indexOf('"',j+7);
					String cstring = strin.substring(j+6, k); // creates a sub string of contents of " "
					
					if (cstring.equalsIgnoreCase("arial")) // if substring is equal to arial puts it in font state
					{
						fontFace = cstring;
						fprop.setSface(cstring);
						fprop.setface(true);
					}
					else if(cstring.equalsIgnoreCase("times new roman"))
					{
						fontFace = cstring;
						fprop.setSface(cstring);
						fprop.setface(true);
					}
					else if(cstring.equalsIgnoreCase("courier"))
					{
						fontFace = cstring;
						fprop.setSface(cstring);
						fprop.setface(true);
					}
					else if(cstring.equalsIgnoreCase("comic san ms"))
					{
						fontFace = cstring;
						fprop.setSface(cstring);
						fprop.setface(true);
					}
					else if(cstring.equalsIgnoreCase("geneva"))
					{
						fontFace = cstring;
						fprop.setSface(cstring);
						fprop.setface(true);
					}
					
				}
			}
		}
		fprops.add(fprop); // adds the font properties to stack
		fcount++;
	}
	
	
	
	
	public Tag parseTag()
	{
		return null;
	}
	
	public Tree getTree()
	{
		return tree;
	}
	
	public void addToTree(Tag currentTag)
	{
		
	}
	
	public void addToTree(Tag currentTag, String tagDate)
	{
		
	}
	
}
